package smartgrid.modules.reputation;

import java.util.HashMap;
import java.util.Map;

public class ImageReputation {
	
	private double								imageInit;
	
	private double								reputationInit;
	
	private double								imageWeight;
	
	private double								reputationWeight;
	
	private Map<Integer, Integer>	compliances;
	
	private Map<Integer, Integer>	violations;
	
	private Map<Integer, Double>	images;
	
	private Map<Integer, Double>	reputations;
	
	
	/**
	 * Constructor
	 * 
	 * @param imageInit
	 *          Initial image value
	 * @param reputationInit
	 *          Initial reputation value
	 * @param imageWeight
	 *          Image weight
	 * @param reputationWeight
	 *          Reputation weight
	 * @return none
	 */
	public ImageReputation(double imageInit, double reputationInit,
			double imageWeight, double reputationWeight) {
		this.imageInit = imageInit;
		this.reputationInit = reputationInit;
		
		// Adjust the Weights
		if (imageWeight < 0) {
			imageWeight *= -1;
		}
		
		if (reputationWeight < 0) {
			reputationWeight *= -1;
		}
		
		if ((imageWeight + reputationWeight) > 1.0) {
			double x = (double) (imageWeight + reputationWeight - 1.0) / 2.0;
			this.imageWeight -= x;
			this.reputationWeight -= x;
		} else if ((imageWeight + reputationWeight) < 1.0) {
			double x = (double) (1.0 - imageWeight - reputationWeight) / 2.0;
			this.imageWeight += x;
			this.reputationWeight += x;
		} else {
			this.imageWeight = imageWeight;
			this.reputationWeight = reputationWeight;
		}
		
		this.compliances = new HashMap<Integer, Integer>();
		this.violations = new HashMap<Integer, Integer>();
		this.images = new HashMap<Integer, Double>();
		this.reputations = new HashMap<Integer, Double>();
	}
	
	
	/**
	 * 
	 */
	public double getImage(int id) {
		double image = this.imageInit;
		
		if (this.images.containsKey(id)) {
			image = this.images.get(id);
		}
		
		return image;
	}
	
	
	/**
	 * 
	 */
	public boolean hasImage(int id) {
		return this.images.containsKey(id);
	}
	
	
	/**
	 * 
	 */
	public void addCompliance(int id) {
		if (!this.compliances.containsKey(id)) {
			this.compliances.put(id, 1);
		} else {
			int numCompliances = this.compliances.get(id);
			this.compliances.put(id, numCompliances + 1);
		}
		
		updateImage(id);
	}
	
	
	/**
	 * 
	 */
	public void addViolation(int id) {
		if (!this.violations.containsKey(id)) {
			this.violations.put(id, 1);
		} else {
			int numViolations = this.violations.get(id);
			this.violations.put(id, numViolations + 1);
		}
		
		updateImage(id);
	}
	
	
	/**
	 * 
	 */
	private void updateImage(int id) {
		double image = this.imageInit;
		
		if ((this.compliances.containsKey(id)) || (this.violations.containsKey(id))) {
			int numCompliances = 0;
			int numViolations = 0;
			
			if (this.compliances.containsKey(id)) {
				numCompliances = this.compliances.get(id);
			}
			
			if (this.violations.containsKey(id)) {
				numViolations = this.violations.get(id);
			}
			
			image = (double) numCompliances
					/ (double) (numCompliances + numViolations);
		}
		
		this.images.put(id, image);
	}
	
	
	/**
	 * 
	 */
	public double getReputation(int id) {
		double value = this.reputationInit;
		
		if (this.reputations.containsKey(id)) {
			value = this.reputations.get(id);
		} else {
			this.reputations.put(id, value);
		}
		
		return value;
	}
	
	
	/**
	 * 
	 */
	public boolean hasReputation(int id) {
		return this.reputations.containsKey(id);
	}
	
	
	/**
	 * Update an agent reputation
	 * 
	 * @param informer
	 *          Reputation informer
	 * @param target
	 *          Reputation target
	 * @param value
	 *          Reputation value of the target
	 * @return none
	 */
	public void updateReputation(int informer, int target, double value) {
		
		double informerImage = this.getImage(informer);
		
		double reputation;
		if (this.reputations.containsKey(target)) {
			reputation = this.reputations.get(target);
			
			if (this.images.containsKey(informer)) {
				reputation += informerImage * value;
				reputation /= 2;
			} else {
				reputation += value;
				reputation /= 2;
			}
			
		} else {
			
			if (this.images.containsKey(informer)) {
				reputation = informerImage * value;
			} else {
				reputation = value;
			}
		}
		
		this.reputations.put(target, reputation);
	}
	
	
	/**
	 * 
	 */
	public double getEvaluation(int id) {
		// Calculate the evaluation
		double eval;
		
		if (this.hasImage(id)) {
			eval = (this.getReputation(id) * this.reputationWeight)
					+ (this.getImage(id) * this.imageWeight);
		} else {
			eval = this.getReputation(id);
		}
		
		return eval;
	}
}